// 绑定 this
let 对象 = {
    姓名: '爱丽斯',
    打招呼() {
        console.log(`你好, ${this.姓名}`);
    }
};

let 打招呼 = 对象.打招呼;
// 打招呼();  // 报错
打招呼 = 打招呼.bind(对象);
打招呼();


// 预设参数
function 求和(a, b, c) {
    return a + b + c;
}
let 偏函数 = 求和.bind(null, 1, 2)
console.log(偏函数(3));


// 多层绑定的 thisArg
function  日志(...参数数组) {
    console.log(this, ...参数数组);
}
let 一层绑定 = 日志.bind('一层的值', 1, 2);
let 二层绑定 = 一层绑定.bind('二层的值', 3, 4);
二层绑定(5, 6);


// 绑定构造函数
class 基类 {
    constructor(...参数数组) {
        console.log(new.target === 基类);
        console.log(参数数组);
    }
}

new 基类(1, 2);
let 绑定类 = 基类.bind(null, 3, 4);
new 绑定类(5, 6);
console.log(new 基类() instanceof 绑定类);
console.log(绑定类.name);


console.log('-'.repeat(25));

// 偏函数
function 列表(...参数数组) {
    return 参数数组;
}

function 参数求和(参数1, 参数2) {
    return 参数1 + 参数2;
}

console.log(列表(1, 2, 3));
console.log(参数求和(1, 2));
let 列表偏函数 = 列表.bind(null, 37);
let 求和偏函数 = 参数求和.bind(null, 37);
console.log(列表偏函数());
console.log(列表偏函数(1, 2, 3));
console.log(求和偏函数(5));
console.log(求和偏函数(5, 10));


console.log('-'.repeat(25));

// 回调函数 this 绑定
class 延迟开花 {
    constructor() {
        this.花瓣数 = Math.floor(Math.random() * 12) + 1;
    }
    开花() {
        setTimeout(
            this.宣布.bind(this),
            1000,
            // function () {console.log(this);}
        );
    }
    宣布() {
        console.log(`我是一朵美丽的花, 拥有 ${this.花瓣数} 个花瓣!`)
    }
}


let 花朵 = new 延迟开花();
花朵.开花();


class 延迟开花1 {
    constructor() {
        this.花瓣数 = Math.floor(Math.random() * 12) + 1;
    }
    开花() {
        setTimeout(
            () => this.宣布(),
            1000,
            // function () {console.log(this);}
        );
    }
    宣布() {
        console.log(`我是一朵美丽的花, 拥有 ${this.花瓣数} 个花瓣!`)
    }
}


花朵 = new 延迟开花1();
花朵.开花();

console.log('-'.repeat(25));
// 作为构造函数使用的绑定函数
class 点 {
    constructor(横坐标, 纵坐标) {
        this.横坐标 = 横坐标;
        this.纵坐标 = 纵坐标;
    }
}

点.prototype.toString = function() {
    return `${this.横坐标}, ${this.纵坐标}`
}

let p = new 点(1, 2);
console.log(p.toString());

const 纵轴点 = 点.bind(null, 0); // 构造函数绑定时 thisArg 会被忽略, 纵轴点仍与点共享原型链
console.log(p instanceof 点);
console.log(p instanceof 纵轴点);
p = new 纵轴点(5);
console.log(p.toString());
console.log(p instanceof 点);
console.log(p instanceof 纵轴点);


// 绑定类
class 基类1 {
    static 基类属性 = '基类属性';
}

class 派生类 extends 基类1 {
    static 派生类属性 = '派生类属性';
}

let 绑定派生类 = 派生类.bind(null);
console.log(绑定派生类.基类属性);
console.log(绑定派生类.派生类属性);